#include "graphdl.h"

void GraphDL::Add(Thing o, int64 delta) {  
  CHECK(inited_);
  indent_++;  
  //if (indent_ == 1)
  if (log_) (*log_)
	      << string(indent_, '-') 
	      << "::Add " << ToString(o) << " " << delta << endl;
  if (IsReusable(o)) {
    AddReusable(o, delta);
  } else {
    AddDefinition(o, delta);
  }
  indent_--;
}

// Add a reusable object
// Figure out if you need to add it's definition
// Add a reference
// In our current method, chances of making a new table = 1 / total_count + 1

// This is the diner name inside the table
uint64 kGDL_DINER = 0xf2398de129742ull;

void GraphDL::AddReusable(Thing o, int64 delta ) {
  int64 new_count = DinerUpdate(table_, kGDL_DINER, o.ID(), delta, dlkey_.ID()); 
  int64 old_count = new_count - delta;
  int64 lower_count = min(new_count, old_count);
  if (log_) (*log_) << string(indent_, '-') 
		    << "::AddReusable " << ToString(o) 
		    << " old=" << old_count << " new=" << new_count << endl;
  
  // You are starting a new table!
  if (lower_count == 0) AddDefinition(o, delta);
}

bool GraphDL::IsReusable(Thing o) {
  return !((o.IsArray()) || (o.IsInteger()));
}

void GraphDL::AddDefinition(Thing o, int64 delta) {
  switch (o.GetType()) {
  case IMMUTABLE_ARRAY:
    {
      Box * b = o.GetBox();
      AddToDL(delta * IntegerDL(b->ArraySize()));
      for (uint64 c=0; c<b->ArraySize(); c++) {
	Add(b->Ref(c), delta);
      }
    }
    break;
  case INTEGER:
    AddToDL(delta * IntegerDL(o.GetInteger()));
    break;
  case TOKEN: 
    AddToDL(delta * IntegerDL(o.GetUnboxedValue()));
    break;
  case SYMBOL:
    // Assume known symbols are in the GDL
    // AddToDL(delta * 1000000);
  default:
    break;
  }
}
